package com.nuo.laboratory.jdk;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.*;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 对象属性对比
 *
 * @author LGC
 * @date 2022/5/6 9:47
 */
public class ContrastService {
    Pattern pattern = Pattern.compile("[0-9]+");

    /**
     * 对比对象
     *
     * @param oldObj 旧对象
     * @param newObj 新对象
     * @param clazz  对象类类型
     * @return 对比后每个修改了对象属性集合
     */
    public List<HandleLogRecordDTO> contrastObj(Object oldObj, Object newObj, Class<?> clazz) {
        List<HandleLogRecordDTO> result = new ArrayList<>();
        if (oldObj == null || newObj == null) {
            return null;
        }
        // clazz不是source的父类直接返回
        if (!clazz.isAssignableFrom(oldObj.getClass())) {
            return null;
        }
        // clazz不是target的父类直接返回
        if (!clazz.isAssignableFrom(newObj.getClass())) {
            return null;
        }
        return contrast(oldObj, newObj, clazz, result);
    }

    private List<HandleLogRecordDTO> contrast(Object oldObj, Object newObj, Class<?> clazz, List<HandleLogRecordDTO> result) {
        if (!"java.lang.Object".equals(clazz.getSuperclass().getName())) {
            contrast(oldObj, newObj, clazz.getSuperclass(), result);
        }
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(HandleLog.class)) {
                try {
                    PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                    Method getMethod = pd.getReadMethod();
                    Object o1 = getMethod.invoke(oldObj);
                    Object o2 = getMethod.invoke(newObj);
                    // 字段值都为空
                    if (Objects.isNull(o1) && Objects.isNull(o2)) {
                        break;
                    }
                    // 字段值改变
                    if (Objects.isNull(o1) || Objects.isNull(o2) || !o1.equals(o2)) {
                        HandleLogRecordDTO changeDTO = new HandleLogRecordDTO();
                        Class enumClazz = field.getAnnotation(HandleLog.class).enumClazz();
                        String separator = field.getAnnotation(HandleLog.class).separator();
                        String timeFormat = field.getAnnotation(HandleLog.class).timeFormat();
                        changeDTO.setTitle(field.getAnnotation(HandleLog.class).title());
                        changeDTO.setOldData(o1);
                        changeDTO.setNewData(o2);
                        // 枚举
                        if (enumClazz != HandleLog.DefaultEnum.class) {
                            // 分隔符
                            if (StrUtil.isNotBlank(separator)) {
                                String[] split1 = o1.toString().split(separator);
                                String[] split2 = o2.toString().split(separator);
                                Set<String> set1 = new HashSet<>(split1.length);
                                Set<String> set2 = new HashSet<>(split2.length);
                                for (String s : split1) {
                                    if (pattern.matcher(s).matches()) {
                                        set1.add(IBaseEnum.getLabelByValue(Integer.valueOf(s), enumClazz));
                                    } else {
                                        set1.add(IBaseEnum.getLabelByValue(s, enumClazz));
                                    }
                                }
                                for (String s : split2) {
                                    if (pattern.matcher(s).matches()) {
                                        set2.add(IBaseEnum.getLabelByValue(Integer.valueOf(s), enumClazz));
                                    } else {
                                        set2.add(IBaseEnum.getLabelByValue(s, enumClazz));
                                    }
                                }
                                changeDTO.setOldData(String.join(",", set1));
                                changeDTO.setNewData(String.join(",", set2));
                            } else {
                                changeDTO.setOldData(IBaseEnum.getLabelByValue(o1, enumClazz));
                                changeDTO.setNewData(IBaseEnum.getLabelByValue(o2, enumClazz));
                            }

                        }
                        // 时间格式化
                        if (o1 instanceof Date && o2 instanceof Date && StrUtil.isNotBlank(timeFormat)) {
                            if (DateUtil.format((Date) o1, timeFormat).equals(DateUtil.format((Date) o2, timeFormat))) {
                                continue;
                            }
                        }
                        if (o1 instanceof Date && StrUtil.isNotBlank(timeFormat)) {
                            changeDTO.setOldData(DateUtil.format((Date) o1, timeFormat));
                        }
                        if (o2 instanceof Date && StrUtil.isNotBlank(timeFormat)) {
                            changeDTO.setNewData(DateUtil.format((Date) o2, timeFormat));
                        }
                        result.add(changeDTO);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    public static void main(String[] args) {
        UserInfo old = UserInfo.builder().id("1").nickname("default").phone("17520485651").gender(GenderEnum.FEMALE.getValue())
                .genders("1,2").birthday(DateUtil.parseDate("2021-09-09")).build();
        old.setName("default");

        UserInfo news = UserInfo.builder().id("1").nickname("zs").phone("17520485652").gender(GenderEnum.MALE.getValue())
                .genders("0").birthday(new Date()).build();
        news.setName("zhangsana");

        ContrastService contrastService = new ContrastService();
        List<HandleLogRecordDTO> list = contrastService.contrastObj(old, news, UserInfo.class);
        for (HandleLogRecordDTO handleLogRecordDTO : list) {
            System.out.println(handleLogRecordDTO);
        }
    }
}

/**
 * 操作
 */
@Data
class HandleLogRecordDTO implements Serializable {
    private static final long serialVersionUID = 1013637125706827289L;
    private String title;
    private Object oldData;
    private Object newData;

}

/**
 * 子类
 */
@EqualsAndHashCode(callSuper = true)
@Data
@Builder
class UserInfo extends BaseUser {
    private String id;

    @HandleLog(title = "昵称")
    private String nickname;
    @HandleLog(title = "手机号")
    private String phone;
    @HandleLog(title = "性别", enumClazz = GenderEnum.class)
    private Integer gender;
    @HandleLog(title = "多性别", enumClazz = GenderEnum.class, separator = ",")
    private String genders;
    @HandleLog(title = "生日")
    private Date birthday;
}

/**
 * 父类
 */
@Data
class BaseUser {
    @HandleLog(title = "姓名")
    private String name;
}

/**
 * 操作日志枚举
 *
 * @author LGC
 */
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface HandleLog {
    /**
     * 标题
     */
    String title() default "";

    /**
     * 枚举类类类型
     */
    Class<?> enumClazz() default HandleLog.DefaultEnum.class;

    /**
     * 分隔符
     */
    String separator() default "";

    /**
     * 时间格式
     */
    String timeFormat() default DatePattern.NORM_DATE_PATTERN;

    @Getter
    @AllArgsConstructor
    enum DefaultEnum {
        /**
         * 默认枚举
         */
        DEFAULT
    }
}

/**
 * 性别枚举
 */
@Getter
@AllArgsConstructor
enum GenderEnum implements IBaseEnum<Integer> {
    /**
     *
     */
    MALE(1, "男"),
    FEMALE(2, "女"),
    UNKNOWN(0, "未知");

    private Integer value;
    private String label;

}

/**
 * 通用枚举
 *
 * @param <T>
 */
interface IBaseEnum<T> {

    T getValue();

    String getLabel();

    /**
     * 根据值获取枚举
     *
     * @param value
     * @param clazz
     * @param <E>   枚举
     * @return
     */
    static <E extends Enum<E> & IBaseEnum> E getEnumByValue(Object value, Class<E> clazz) {
        Objects.requireNonNull(value);
        EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
        E matchEnum = allEnums.stream()
                .filter(e -> ObjectUtil.equal(e.getValue(), value))
                .findFirst()
                .orElse(null);
        return matchEnum;
    }

    /**
     * 根据文本标签获取值
     *
     * @param value
     * @param clazz
     * @param <E>
     * @return
     */
    static <E extends Enum<E> & IBaseEnum> String getLabelByValue(Object value, Class<E> clazz) {
        Objects.requireNonNull(value);
        EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
        E matchEnum = allEnums.stream()
                .filter(e -> ObjectUtil.equal(e.getValue(), value))
                .findFirst()
                .orElse(null);

        String label = null;
        if (matchEnum != null) {
            label = matchEnum.getLabel();
        }
        return label;
    }


    /**
     * 根据值获取文本标签
     *
     * @param label
     * @param clazz
     * @param <E>
     * @return
     */
    static <E extends Enum<E> & IBaseEnum> Object getValueByLabel(String label, Class<E> clazz) {
        Objects.requireNonNull(label);
        EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
        String finalLabel = label;
        E matchEnum = allEnums.stream()
                .filter(e -> ObjectUtil.equal(e.getLabel(), finalLabel))
                .findFirst()
                .orElse(null);

        Object value = null;
        if (matchEnum != null) {
            value = matchEnum.getValue();
        }
        return value;
    }
}
